/***************************************************************************
 *   Copyright (C) 2008, 2014 by Philipp Müller                            *
 *   philipp.mueller@gmx.de                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA          *
 ***************************************************************************/

#include <QtNetwork>

#include "vdrfindserver.h"

VDRFindServer::VDRFindServer( QObject *parent ) : QObject( parent )
{
    m_udpSocket = new QUdpSocket( this );

    m_socketEstablished = m_udpSocket->bind( 3024 );

    if ( !m_socketEstablished )
    {
        qWarning() << "VDRFindServer::VDRFindServer: bind to socket failed";
        return;
    }
    else
    {
        qDebug() << "VDRFindServer::VDRFindServer socket established";
    }

    connect( m_udpSocket, SIGNAL( readyRead() ),
             this,        SLOT( processPendingDatagrams() ) );

    connect( this,        SIGNAL( requestBroadcastDatagram() ),
             this,        SLOT( broadcastDatagram() ) );
}


VDRFindServer::~VDRFindServer()
{
    qDebug() << "VDRFindServer::~VDRFindServer() called";
    delete m_udpSocket;
}


QList<serverEntry> VDRFindServer::serverList()
{
    qDebug()<<"socket isValid:"<<m_udpSocket->isValid();
    emit requestBroadcastDatagram();

    //Called from outside
    m_mutex.lock();

    m_waitingForServerFound.wait( &m_mutex );

    m_mutex.unlock();

    return m_serverList;
}


bool VDRFindServer::socketEstablished()
{
    QMutexLocker locker( &m_mutex );
    return m_socketEstablished;
}


void VDRFindServer::processPendingDatagrams()
{
    qDebug() << "VDRFindServer::ProcessPendingDiagrams called";
    QHostAddress sender;

    while ( m_udpSocket->hasPendingDatagrams() )
    {
        QByteArray datagram;
        quint16 senderPort;

        datagram.resize( m_udpSocket->pendingDatagramSize() );
        m_udpSocket->readDatagram( datagram.data(), datagram.size(), &sender, &senderPort );
        int textSize = datagram.size() -9 -1 -16 -2 -4;

        qDebug() << "VDRFindServer::processPendingDatagrams: Got UDP reply from: " << sender.toString() << "| Port:" << senderPort << "  and size: " << datagram.size();

        if ( datagram.startsWith( "VDP-0002" ) ) //If message is "VDP-0001", it's my own message, ignore
        {
            QByteArray adressSnippet = datagram.mid( 10, 16 );
            datagram.remove( 10, 16 ); //
            datagram.remove( 0, 9 );

            QDataStream  stream ( datagram );

            serverEntry  entry;

            stream >> entry.hostNetworkType;

            switch ( entry.hostNetworkType )
            {
                case 0:
                    qDebug() << "VDRFindServer::processPendingDatagrams: server address type not specified";
                    entry.hostAddress = sender;
                    break;
                case 4:
                    qDebug() << "VDRFindServer::processPendingDatagrams: server address type IPV4";
                    entry.hostAddress = QHostAddress ( QString( adressSnippet.left( 4 ) ) );
                    break;
                case 6:
                    qDebug() << "VDRFindServer::processPendingDatagrams: server address type IPV6";
                    entry.hostAddress = QHostAddress ( QString( adressSnippet ) );
                    break;
                default:
                    qWarning() << "VDRFindServer::processPendingDatagrams: Wrong type information: " << entry.hostNetworkType;
                    entry.hostAddress = QHostAddress();
                    break;
            }

            stream >> entry.hostPort;
            stream >> entry.hostProtocolVersion;
            readString( stream, entry.hostName, textSize );

            qDebug() << "VDRFindServer::Entry in List ip:" << entry.hostAddress << "  name: " << entry.hostName << "  port:" << entry.hostPort;

            bool found = false;
            for ( int i = 0; i < m_serverList.size(); ++i )
            {
                if ( m_serverList[i].hostAddress == entry.hostAddress )
                {
                    found = true;
                }
            }

            if ( !found )
            {
                m_serverList.append( entry );
            }
        }
        else
        {
            qWarning() << "UDP package reseved, which doesn't start with VDP-0002";
        }
    }
}


void VDRFindServer::broadcastDatagram()
{
    QByteArray datagram = "VDP-0001";
    m_udpSocket->writeDatagram( datagram.data(), datagram.size(), QHostAddress::Broadcast, 51051 );
    m_udpSocket->writeDatagram( datagram.data(), datagram.size(), QHostAddress::Broadcast, 51052 );
    m_udpSocket->writeDatagram( datagram.data(), datagram.size(), QHostAddress::Broadcast, 51053 );
    m_udpSocket->writeDatagram( datagram.data(), datagram.size(), QHostAddress::Broadcast, 51054 );
    m_udpSocket->writeDatagram( datagram.data(), datagram.size(), QHostAddress::Broadcast, 51055 );
    QTimer::singleShot( 500, this, SLOT( endWait() ) );
    qDebug() << "VDRFindServer::Broadcasted 'VDP-0001'";
}


void VDRFindServer::endWait()
{
    m_mutex.lock();
    m_waitingForServerFound.wakeAll();
    m_mutex.unlock();
}


bool VDRFindServer::readString( QDataStream& in, QString &string, int length )
{
    string.clear();
    bool finished = false;

    for ( int i = 0; i < length; i++ )
    {
        quint8 a;
        in >> a;

        if ( a == 0 )
        {
            if ( length == 0 ) //undefined length
            {
                return true; //we found trailing 0 and can return
            }
            finished = true;
        }
        if ( !finished )
        {
            string.append( a );
        }
//        qDebug() << "VDR::readString: in-value ("<<i<<"): " << a <<"  text" << QString((QChar)a);
    }

    return finished;
}
